#include "globals.h"

/**
 * The message send API
 * @param sockfd supplies the socket descriptor for write
 * @param adddr supplies the canonical IP address for the destination node
 * @param portno supplies the destination port number
 * @param msg supplies the message to be sent
 * @param flag supplies if a route rediscovery to the destination node already exists
 */
void 
msg_send(
        int sockfd, 
        char addr[INET_ADDRSTRLEN], 
        int portno, 
        char msg[], 
        int flag
        )
{
    char char_seq[1000] = "\0"; // (char *) malloc(strlen(msg) + 30);
    char temp[20];
    struct sockaddr_un odraddr;
    int tsock = Socket(AF_LOCAL, SOCK_DGRAM, 0);
    int err;
    struct sockaddr_un cli_rcv_addr;
    int cli_rcv_addr_len;

    cli_rcv_addr_len = sizeof(cli_rcv_addr);
    bzero(&cli_rcv_addr, cli_rcv_addr_len);

    Getsockname(sockfd, (SA*) &cli_rcv_addr, &cli_rcv_addr_len);

    sprintf(char_seq, "%d%s", sockfd," ");  
    sprintf(temp, "%d%s", portno, " "); 
    strcat(char_seq, temp);
    sprintf(temp, "%d%s", flag, " ");   
    strcat(char_seq, temp);
    strcat(char_seq, addr);
    strcat(char_seq," ");

    strcat(char_seq, cli_rcv_addr.sun_path);
    strcat(char_seq, " ");

    strcat(char_seq, msg);
    strcat(char_seq,"\0");

    bzero(&odraddr, sizeof(odraddr));   
    odraddr.sun_family = AF_LOCAL;

    if (portno == SERV_PORT)
    {
        //
        // Client is calling me.
        //
        
        strcpy(odraddr.sun_path, CLI_DG_PATH);
    }
    else
    {
        //
        // Server is calling me.
        //
 
        strcpy(odraddr.sun_path, SRV_DG_PATH);
    }

    // printf("MSG to be sent %s\n", char_seq);
    // printf("\nODR ADDR%s\n", odraddr.sun_path);
    err = sendto(tsock, char_seq, 1000, 0, (SA *) &odraddr, sizeof(odraddr));

    if (err < 0)
    {
        printf("Send to error while sending from odr.\n");
    }
}

/**
  Creates and binds a raw socket for each of the netwrok interface. The
  interfaces 'lo' and 'eth0' are ignored.

  @return the number of sockets created.
 */
int 
create_raw_sockets(
        sock_desc sockets[]
        )
{
    int i;
    int ix = 0;
    int jx;
    int err;

    struct hwa_info	*hwa, *hwahead;
    char   *ptr;

    char LO_NAME[3] = "lo";
    char ETH0_NAME[5] = "eth0";
    struct sockaddr	*sa;

    printf("\n");

    for (hwahead = hwa = Get_hw_addrs(), ix = 0; hwa != NULL; hwa = hwa->hwa_next) 
    {
        //
        // Ignore the 'lo' and 'eth0' interfaces.
        //

        if (!strncmp(hwa->if_name, LO_NAME, 2) || 
                !strncmp(hwa->if_name,ETH0_NAME, 4))
        {
            continue;
        }

        printf("%s :%s", hwa->if_name, ((hwa->ip_alias) == IP_ALIAS) ? " (alias)\n" : "\n");

        if ( (sa = hwa->ip_addr) != NULL)
        {	
            printf("         IP addr = %s\n", Sock_ntop_host(sa, sizeof(*sa)));
        }

        printf("         HW addr = ");
        ptr = hwa->if_haddr;
        i = IF_HADDR;

        sockets[ix].sockfd = socket(AF_PACKET, SOCK_RAW, htons(PROTO_NUM));
        check_err(sockets[ix].sockfd, "socket()");

        bzero(&sockets[ix].socket_address, sizeof(sockets[ix].socket_address));	

        sockets[ix].socket_address.sll_family = AF_PACKET;
        sockets[ix].socket_address.sll_ifindex = hwa->if_index;

        sockets[ix].ifindex = hwa->if_index;

        sockets[ix].socket_address.sll_protocol = htons(PROTO_NUM);
        sockets[ix].socket_address.sll_halen = ETH_ALEN;
        sockets[ix].socket_address.sll_addr[6] = 0x00;
        sockets[ix].socket_address.sll_addr[7] = 0x00;

        jx = 0;

        do {
            sockets[ix].socket_address.sll_addr[jx] = (*ptr++ & 0xff);
            printf("%.2x%s", sockets[ix].socket_address.sll_addr[jx], (i == 1) ? " " : ":");
            sockets[ix].mac_addr[jx] = sockets[ix].socket_address.sll_addr[jx];
            jx++;
        } while (--i > 0);

        printf("\n");

        printf("Interface no = %d\n", hwa->if_index);
	err = bind(sockets[ix].sockfd, (struct sockaddr *) &sockets[ix].socket_address, sizeof(sockets[ix].socket_address));
        check_err(err,"bind RAW Socket");	

        ix++;
    }

    return ix;
}

/**
  Function to flood an interface.

  @param sock supplies the socket descriptor structure of the raw socket.
  @param data_to_send supplies a pointer to the data that will be flooded.
  @param len_data supplies the size of the data pointed to by #data_to_send.
 */
void
flood_interface(
        sock_desc sock,
        void * data_to_send,
        size_t len_data
        )
{
    int err;

    unsigned char dest_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

    //
    // Buffer for the ethernet frame.
    //

    void* buffer = (void*)malloc(ETH_FRAME_LEN);

    struct sockaddr_ll * addr = (struct sockaddr_ll*) malloc(sizeof(struct sockaddr_ll));

    //
    // pointer to ethenet header
    // 

    unsigned char* etherhead = buffer;

    //
    // userdata in ethernet frame
    //

    unsigned char* data = buffer + 14;

    //
    // another pointer to ethernet header
    //

    struct ethhdr *eh = (struct ethhdr *)etherhead;

    bzero(addr, sizeof(addr));

    //
    // Prepare sockaddr_ll
    //

    addr->sll_family   = PF_PACKET;	
    addr->sll_protocol = htons(PROTO_NUM);	
    addr->sll_ifindex  = sock.ifindex;
    addr->sll_hatype   = ARPHRD_ETHER;
    addr->sll_pkttype  = PACKET_OTHERHOST;
    addr->sll_halen    = ETH_ALEN;		

    //
    // Set MAC for flooding. 
    //

    addr->sll_addr[0]  = 0xFF;		
    addr->sll_addr[1]  = 0xFF;		
    addr->sll_addr[2]  = 0xFF;
    addr->sll_addr[3]  = 0xFF;
    addr->sll_addr[4]  = 0xFF;
    addr->sll_addr[5]  = 0xFF;

    addr->sll_addr[6]  = 0x00;
    addr->sll_addr[7]  = 0x00;

    //
    // Set the frame header.
    //

    memcpy((void*)buffer, (void*)dest_mac, ETH_ALEN);
    memcpy((void*)(buffer+ETH_ALEN), (void*)sock.mac_addr, ETH_ALEN);
    eh->h_proto = htons(PROTO_NUM);

    memcpy(data, data_to_send, len_data);

    printf ("Sending data. Type: %d \n", *data);

    err = sendto(sock.sockfd, buffer, ETH_FRAME_LEN, 0, (struct sockaddr*) addr, sizeof(*addr));

    printf("Broadcast successful.\n");

    check_err(err, "sendto");
}

/**
  Floods all interfaces of the network.

  @param sockfd supplies the socket descriptor of the raw socket.
  @param data_to_send supplies a pointer to the data that will be flooded.
  @param len_data supplies the size of the data pointed to by #data_to_send.
 */
void 
flood_all_interfaces(
        sock_desc sockets[],
        int num_socks,
        void * data_to_send,
        size_t len_data
        )
{
    int i;

    for ( i = 0; i < num_socks; i++)
    {
        flood_interface(sockets[i], data_to_send, len_data);
    }
}

/**
  Sends a unicast message from source to destination using raw socket.

  @param sockfd supplies the socket descriptor of the raw socket.
  @param src_mac supplies the mac address of the source interface.
  @param dest_mac supplies the mac address of the destination interface.
  @param ifindex supplies the device index for the interface.
  @param data_to_send supplies a pointer to the data that will be sent.
  @param len_data supplies the size of the data pointed to by #data_to_send.
 */
void
send_unicast(
        int sockfd,
        char src_mac[],
        char dest_mac[],
        int ifindex,
        void * data_to_send,
        size_t len_data
        )
{
    int err;
    int ix;

    struct sockaddr_ll * addr = (struct sockaddr_ll*) malloc(sizeof(struct sockaddr_ll));

    //
    // Buffer for the ethernet frame.
    //

    void* buffer = (void*)malloc(ETH_FRAME_LEN);

    //
    // pointer to ethenet header
    // 

    unsigned char* etherhead = buffer;

    //
    // userdata in ethernet frame
    //

    unsigned char* data = buffer + 14;

    //
    // another pointer to ethernet header
    //

    struct ethhdr *eh = (struct ethhdr *)etherhead;

    bzero(addr, sizeof(addr));

    //
    // Prepare sockaddr_ll
    //

    addr->sll_family   = PF_PACKET;	
    addr->sll_protocol = htons(PROTO_NUM);	
    addr->sll_ifindex  = ifindex;
    addr->sll_hatype   = ARPHRD_ETHER;
    addr->sll_pkttype  = PACKET_OTHERHOST;
    addr->sll_halen    = ETH_ALEN;		

    //
    // Set MAC for flooding. 
    //

    for (ix  = 0; ix < 6; ix++)
    {
        addr->sll_addr[ix] = dest_mac[ix];
    }

    addr->sll_addr[6]  = 0x00;
    addr->sll_addr[7]  = 0x00;

    //
    // Set the frame header.
    //

    memcpy((void*)buffer, (void*)dest_mac, ETH_ALEN);
    memcpy((void*)(buffer+ETH_ALEN), (void*)src_mac, ETH_ALEN);
    eh->h_proto = htons(PROTO_NUM);

    memcpy((void *)data,(void *)data_to_send, len_data);

    err = sendto(sockfd, buffer, ETH_FRAME_LEN, 0, (struct sockaddr*) addr, sizeof(*addr));
    check_err(err, "sendto");
}

/**
  Parses the character stream sent by #msg_send.

  @param msg        supplies the message to be parsed.
  @param clisockfd  supplies the pointer to the client socket fd that will be
  parsed.
  @param ip_addr    supplies the pointer to the IP Address string that will be
  parsed.
  @param portno     supplies the pointer to the client port number that will be
  parsed.
  @param flag       supplies the pointer to the client socket flags that will be
  parsed.

  @return the message to be sent, parsed from the character stream.                   
 */
char *  
parse_msg_send_data( 
        char msg[1000],
        int * clisockfd,
        char ip_addr[INET_ADDRSTRLEN],
        int * portno,
        int * flag,
        char * cli_sun_path
        )
{
    char * temp = (char *) malloc (1000);
    int c = 0;
    int i;
    int j;

    bzero(temp, 1000);

    for (i = 0, j = 0 ; i < strlen(msg); i++)
    {
        if((msg[i] == ' ' && c != 5) || (i == strlen(msg) - 1))
        {
            c++;
            if(i == strlen(msg) - 1)
            {
                temp[j] = msg[i];
                j++;
            }
            temp[j] = '\0';
            switch(c)
            {
                case 1:
                    * clisockfd = atoi(temp);
                    break;			
                case 2:
                    * portno = atoi(temp);
                    break;			
                case 3:
                    * flag = atoi(temp);
                    break;			
                case 4:
                    strcpy(ip_addr, temp);
                    break;	
                case 5:
                    strcpy(cli_sun_path, temp);
                    break;
                case 6:
                    return temp;

            }	
            j = 0;
        }
        else
        {
            temp[j] = msg[i];
            j++;
        }	
    }

    return temp;
}

/** 
  Gets the canonical IP address of the vm on which the ODR process is running
  on.

  @return the canonical IP address string, in presentation format.
 */
char*
get_odr_ip()
{
    struct hwa_info	*hwa, *hwahead;

    char ETH0_NAME[5] = "eth0";
    struct sockaddr	*sa;

    char * ip_addr = (char*) malloc(INET_ADDRSTRLEN + 1);

    bzero(ip_addr, INET_ADDRSTRLEN + 1);

    for (hwahead = hwa = Get_hw_addrs(); hwa != NULL; hwa = hwa->hwa_next) 
    {
        if (strncmp(hwa->if_name, ETH0_NAME, 4) == 0)
        {
            if ( (sa = hwa->ip_addr) != NULL)
            {	
                strcpy(ip_addr, Sock_ntop_host(sa, sizeof(*sa)));
            }

            break;
        }
    }

    return ip_addr;
}

/**
  Generates a RREQ packet to be sent.
 */
RREQ_hdr *
generate_rreq(
        char src_id[],
        char dst_id[],
        int * odr_broadcast_id,
        int disc_flag
        )
{
    RREQ_hdr * rreq_packet = (RREQ_hdr *) malloc(sizeof(RREQ_hdr));
    int ix = 0;

    rreq_packet->broadcast_id = htons(*odr_broadcast_id);

    (*odr_broadcast_id)++;

    rreq_packet->type = htons(0);
    rreq_packet->rrep_sent = htons(0);
    rreq_packet->hop_count = htons(1);

//    strcpy(rreq_packet->source_id, src_id);
//    strcpy(rreq_packet->dest_id, dst_id);	
	Inet_pton(AF_INET, src_id, &rreq_packet->source_id.s_addr);
	Inet_pton(AF_INET, dst_id, &rreq_packet->dest_id.s_addr);

    rreq_packet->forced_disc = htons(disc_flag);

    return rreq_packet;
}

RREP_hdr *
generate_rrep(
        char src_id[],
        char dst_id[],
        int * broadcast_id	
        )	
{

    RREP_hdr * rrep_packet = (RREP_hdr *) malloc(sizeof(RREP_hdr));

    rrep_packet->type = htons(1);
    rrep_packet->broadcast_id = htons(* broadcast_id);
    rrep_packet->hop_count = htons(1);

    //strcpy(rrep_packet->source_id, src_id);
    //strcpy(rrep_packet->dest_id, dst_id);
	Inet_pton(AF_INET, src_id, &rrep_packet->source_id.s_addr);
	Inet_pton(AF_INET, dst_id, &rrep_packet->dest_id.s_addr);

    return rrep_packet;
}

void
process_recvd_rreq(
        sock_desc sockets[],
        int ix,
        void * buffer,
        broadcast_list * bl_head,
        table * route_tab,
        char my_ip[],
        int num_sockets,
	int staleness,
        RREP_hdr * rrep_packet
        )
{
    RREQ_hdr * recvd_rreq = (RREQ_hdr*) malloc(sizeof(RREQ_hdr));
    struct timeval ts;
    int ifi = sockets[ix].ifindex;
    char  * next_hop = (char *)malloc(6);
    node * n;
    int i;
    int check_stale;
	char source_id[INET_ADDRSTRLEN];
	char dest_id[INET_ADDRSTRLEN];

    memcpy((void*)recvd_rreq, (void*)(buffer + 14), sizeof(RREQ_hdr));
	
	Inet_ntop(AF_INET, &recvd_rreq->source_id.s_addr, source_id, sizeof(source_id));
	Inet_ntop(AF_INET, &recvd_rreq->dest_id.s_addr, dest_id, sizeof(dest_id));
    //
    // Our current time stamp. Used to update the routing table in case a newer 
    // request is received.
    //

    gettimeofday(&ts, NULL);

    //
    // Extract the mac address of the machine that sent the request.
    //

    memcpy((void *)next_hop, buffer + 6, 6);

    //
    // Check if we have already received this broadcast req.
    //
   
     int b_id = ntohs(recvd_rreq->broadcast_id);

    printf("source:%s dest:%s hop_count:%d ifi:%d ts:%u\n", source_id,
            dest_id,  ntohs(recvd_rreq->hop_count), ifi, ts.tv_sec);

    if(strcmp(my_ip, source_id) == 0)
    {
        printf("\nODR got back the RREQ it broadcasted ... This RREQ is discarded\n");
        return;
    }

    //
    // Check if the routing table contains an entry for this destination ip address.
    //

    n = find_entry(route_tab, dest_id);

    if( n == NULL ||  strncmp(my_ip, dest_id, INET_ADDRSTRLEN) == 0 || (n != NULL && (check_stale =  isstale(n->timestamp, staleness, n->dest))))
    { 
    
		if(n != NULL && check_stale)
		{
			//
			// Remove the stale entry
			//			
			printf("Stale Entry for Destination %s has been removed from the Routing table\n", n->dest);
			remove_entry(route_tab, n->dest);
		}					

    
        //
        //	rrep_indicator is set to 1 when the current node is the destination and
        //	RREQ gives a fresh or new route
        //

        int rrep_indicator = 0;


        //
        // No Entry in routing table for Destination or Destination Reached.
        //

        node * n1;
        n = make_node(source_id, next_hop, ifi, ntohs(recvd_rreq->hop_count), ts);

        //
        // Check if we have a backward pointer to the source address in the table.
        //

        if ((n1 = find_entry(route_tab, source_id)) == NULL)
        {
            //
            // Add reverse entry to source in Routing table.
            //

            add_entry(route_tab, n);
            rrep_indicator = 1;
        }
        else
        {
            if(n1->timestamp.tv_sec < n->timestamp.tv_sec && 
                    n1->hop_count >= n->hop_count)
            {																
                //
                // Fresh Route : since hop count in RREQ is lesser than or 
                // equal than the entry in Routing table. Update the entry in
                // the Routing table.
                //

                update_entry(route_tab, n);
                rrep_indicator = 1;
            }
        }

        print_table(route_tab);		

        if (strncmp(my_ip, dest_id, INET_ADDRSTRLEN) != 0)
        {

            if (find_bl(source_id, ntohs(recvd_rreq->broadcast_id), bl_head) == 0)
            {
                return;
            }

            //
            // Broadcast list table does not contain any entry for this set of broadcast 
            // id and source id. So, just insert to the table to avoid redundant
            // broadcasts in future.
            //
            insert_bl(create_br(source_id, &b_id),
                    bl_head);


            //
            // Destination not reached. Increment the hop count and broadcast
            // again.
            //
            recvd_rreq->hop_count = htons(ntohs(recvd_rreq->hop_count) + 1);																		
            memcpy((void*)(buffer + 14), (void*)recvd_rreq, sizeof(RREQ_hdr));

            for(i = 0; i < num_sockets; i++)
            {
                if(i != ix)
                {
                    //
                    // Flood other interfaces.
                    //

                    flood_interface(sockets[i], (buffer + 14), sizeof(RREQ_hdr));														
                }
            }
        }
        else
        {
            //
            // Destination Reached
            //

            printf("RREQ reached Destination Node\n");

            if(ntohs(recvd_rreq->rrep_sent) == 1)
            {	
                printf("\nRREP_sent flag set in RREQ ... RREP not sent\n");
                return;		
            }

            if(rrep_indicator == 1)	
            {

                printf("RREQ received for a new route or same route with a smaller hop count\n");
                rrep_packet = generate_rrep(my_ip, source_id, 
                       & b_id);

                n = find_entry(route_tab, source_id); 

                printf("Hop count: %d\n", n->hop_count);					

                send_unicast(sockets[ix].sockfd, sockets[ix].mac_addr,n->next_hop, 
                        sockets[ix].ifindex, (void*) rrep_packet, sizeof(RREP_hdr));

                printf("RREP sent.\n");
            }
            else
            {
                printf("Duplicate RREQ Received. RREP not sent \n");               
            }
        }
    }
    else
    {
        //
        // Send RREP
        //

        // TODO

        printf("\nRouting table contains the entry for dest[%s]\n", dest_id);

        node * n1;
        n = make_node(source_id, next_hop, ifi, ntohs(recvd_rreq->hop_count), ts);


        if ((n1 = find_entry(route_tab, source_id)) == NULL)
        {
            //
            // Add reverse entry to source in Routing table.
            //

            printf("Adding reverse entry in routing table for %s.\n", n->dest);

            add_entry(route_tab, n);
        }
        else
        {
            if(n1->timestamp.tv_sec < n->timestamp.tv_sec && 
                    n1->hop_count >= n->hop_count)
            {																
                //
                // Fresh Route : since hop count in RREQ is lesser than or 
                // equal than the entry in Routing table. Update the entry in
                // the Routing table.
                //

                printf("Updating entry in routing table for %s.\n", n->dest);
                
                update_entry(route_tab, n);
            }
        }

        print_table(route_tab);

        if(ntohs(recvd_rreq->rrep_sent) != 1)
        {
            rrep_packet = generate_rrep(dest_id, source_id, 
                   & b_id);

            n = find_entry(route_tab, source_id);

            rrep_packet->hop_count = htons(n->hop_count + 1); 

            recvd_rreq->rrep_sent = htons(1);
           
            send_unicast(sockets[ix].sockfd, sockets[ix].mac_addr, n->next_hop, 
                    sockets[ix].ifindex, (void*) rrep_packet, sizeof(RREP_hdr));

            printf("\nRREP sent.\n");
        }
        else
        {
            printf("\nRREP_sent flag set in RREQ ... RREP not sent\n");
            return;	
        }

        recvd_rreq->hop_count = htons(ntohs(recvd_rreq->hop_count) + 1);

        memcpy((void*)(buffer + 14), (void*)recvd_rreq, sizeof(RREQ_hdr));

        for(i = 0; i < num_sockets; i++)
        {
            if(i != ix)
            {
                //
                // Flood other interfaces.
                //

                flood_interface(sockets[i], (buffer + 14), sizeof(RREQ_hdr));														
            }
        }	
    }
}

void
process_recvd_rrep(
        sock_desc sockets[],
        int ix,
        void * buffer,
        table * route_tab,
        char my_ip[],
        int num_sockets,
        char dest_ip_addr[],
        int source_port,
        int dest_port,
        char data_from_cli[]
        )
{
    RREP_hdr * recvd_rrep = (RREP_hdr*) malloc(sizeof(RREP_hdr));
    struct timeval ts;
    int ifi = sockets[ix].ifindex;
    char  * next_hop = (char *)malloc(6);
    node * n, * n1;
    int i;
    APP_hdr* app_data = NULL;
	char source_id[INET_ADDRSTRLEN];
	char dest_id[INET_ADDRSTRLEN];

    memcpy((void*)recvd_rrep, (void*)(buffer + 14), sizeof(RREP_hdr));

	Inet_ntop(AF_INET, &recvd_rrep->source_id.s_addr, source_id, sizeof(source_id));
	Inet_ntop(AF_INET, &recvd_rrep->dest_id.s_addr, dest_id, sizeof(dest_id));
    
	gettimeofday(&ts, NULL);

    memcpy((void *)next_hop, buffer + 6, 6);

    n = make_node(source_id, next_hop, ifi, ntohs(recvd_rrep->hop_count), ts);

    if((n1 = find_entry(route_tab, source_id)) == NULL)
    {
        //
        // Add Forward entry to Destination in Routing table.
        //

        printf("Adding entry to the routing table for %s\n.", source_id);
        add_entry(route_tab, n);
    }
    else
    {
        if (n1->timestamp.tv_sec < n->timestamp.tv_sec && 
                n1->hop_count >= n->hop_count)
        {																
            //
            // Fresh Route : Update Route
            //

            printf("Updating entry for %s.\n", source_id);
            update_entry(route_tab, n);
        }
    }

    //
    // Unicast the RREP to the one-hop neighbor who knows the Destination
    //

    print_table(route_tab);			

    if(strcmp(dest_id, my_ip) != 0)
    {

        //
        // Destination for RREP Unreached
        //

        printf("Destination has not been reached.\n");

        recvd_rrep->hop_count = htons(ntohs(recvd_rrep) + 1);																			
        n = find_entry(route_tab, dest_id); 

        for(i = 0; i < num_sockets; i++ )
        {
            if(sockets[i].ifindex == n->ifindex)
            {
                break;
            }
        }

        send_unicast(sockets[i].sockfd, sockets[i].mac_addr, n->next_hop, 
                sockets[i].ifindex, (void*) recvd_rrep, sizeof(RREP_hdr));

        printf("RREP Unicasted to Destination %d\n", ntohs(recvd_rrep->type));
    }
    else
    {
        //
        // Send Application Payload
        //

        printf("RREP Reached Destination node.\n");

        n = find_entry(route_tab, dest_ip_addr);

        printf("Prepping app data. source_id: %s, dest_id: %s\n",
                source_id, dest_ip_addr);

        printf("Source port: %d, Destination port: %d.\n", source_port,
                dest_port);

        app_data = create_app_data(get_odr_ip(), source_port,
                dest_ip_addr, dest_port, ntohs(recvd_rrep->hop_count),
                strlen(data_from_cli), data_from_cli);

        printf("Sending application data packet to the next hop on interface: %d.\n", 
                sockets[ix].ifindex);

        send_unicast(sockets[ix].sockfd, sockets[ix].mac_addr, n->next_hop,
                sockets[ix].ifindex, (void*) app_data, sizeof(APP_hdr));
    }
}

APP_hdr*
create_app_data(
        char source_id[],
        int source_port,
        char dest_id[],
        int dest_port,
        int hop_count,
        int data_len,
        char data[]
        )
{
    APP_hdr* app_data = (APP_hdr *) malloc(sizeof(APP_hdr));

    app_data->type = htons(2);
    //strcpy(app_data->source_id, source_id);
    app_data->source_port = htons(source_port);
    //strcpy(app_data->dest_id, dest_id);
    app_data->dest_port = htons(dest_port);
    app_data->hop_count = htons(1); //hop_count;
    app_data->data_len = htons(data_len);
    strncpy(app_data->data, data, data_len);
	Inet_pton(AF_INET, source_id, &app_data->source_id.s_addr);
	Inet_pton(AF_INET, dest_id, &app_data->dest_id.s_addr);

    return app_data;
}

void
process_recvd_apphdr(
        sock_desc sockets[],
	int ix,
        void * buffer,
        table * route_tab,
        char my_ip[],
        cli_log * cli_info,
	int num_sockets
        )
{
    APP_hdr * app_data = (APP_hdr *) malloc(sizeof(APP_hdr));
    node *n = NULL, * n1 = NULL;
    int sockfd;
    char filename[100];// = "/tmp/byebye.dg";
    struct sockaddr_un serv_addr;
    struct timeval ts;
    int ifi = sockets[ix].ifindex;
    char  * next_hop = (char *)malloc(6);
    int i;	
    int err;
	char source_id[INET_ADDRSTRLEN];
	char dest_id[INET_ADDRSTRLEN];

    memcpy((void*) app_data, (void*) (buffer + 14), sizeof(APP_hdr));

	Inet_ntop(AF_INET, &app_data->source_id.s_addr, source_id, sizeof(source_id));
	Inet_ntop(AF_INET, &app_data->dest_id.s_addr, dest_id, sizeof(dest_id));
    
	gettimeofday(&ts, NULL);

    memcpy((void *)next_hop, (void *)(buffer + 6), 6);
    
    n = make_node(source_id, next_hop, ifi, ntohs(app_data->hop_count), ts);

    if((n1 = find_entry(route_tab, source_id)) == NULL)
    {
        //
        // Add Forward entry to Destination in Routing table.
        //

        printf("Adding entry to the routing table for %s\n.", source_id);
        add_entry(route_tab, n);
    }
    else
    {
        if (n1->timestamp.tv_sec < n->timestamp.tv_sec && 
                n1->hop_count >= n->hop_count)
        {																
            //
            // Fresh Route : Update Route
            //

            printf("Updating entry for %s.\n", source_id);
            update_entry(route_tab, n);
        }
    }

    print_table(route_tab);

    if (strcmp(my_ip, dest_id) == 0)
    {
        //
        // App data has reached the destination. We are done here.
        //

        printf("App data reached destination.\n");

        printf("Received: %s, %d\n", app_data->data, ntohs(app_data->dest_port));

        sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);

        if (ntohs(app_data->dest_port) == SERV_PORT)
        {
            //
            // Client sent the data. Send char stream to server.
            //

           strcpy(filename, SRV_WELL_KNOWN_PATH);
        
	   printf("Sending data to Server - source_id: %s, dest_id: %s source port:
                %d, dest_port: %d\n", source_id, dest_id,
                ntohs(app_data->source_port), ntohs(app_data->dest_port));
        }
        else
        {
            //
            // Server sent the data. Send char stream to the client.
            //

           strcpy(filename, find_client_sunpath(cli_info,ntohs(app_data->dest_port)));
	   delete_client(cli_info,ntohs(app_data->dest_port));

           printf("Sending data to Client - source_id: %s, dest_id: %s source port:
                %d, dest_port: %d\n", source_id, dest_id,
               ntohs(app_data->source_port), ntohs(app_data->dest_port));

        }

        // printf("sun_path: %s\n", filename);
        bzero(&serv_addr, sizeof(serv_addr));
        serv_addr.sun_family = AF_LOCAL;
        strcpy(serv_addr.sun_path, filename);
        //
        // Create a char stream of data to be sent to the server and send it
        // using a unix datagram socket.
        //


        err = sendto(sockfd, create_stream_msg_recv(app_data), 1000, 0,
                (SA*)&serv_addr, sizeof(serv_addr));

        if (err < 0)
        {
            printf("Sendto error.\n");
        }
        return;
    }



    n = NULL;	

    n = find_entry(route_tab, dest_id);

    app_data->hop_count = htons(ntohs(app_data->hop_count) + 1);	

    if (n == NULL)
    {
        err_sys("Entry absent in Routing table.\n");
        exit(1);
    }

    //
    // Forward the app data.
    //

    printf("Forwarding app data to next hop on interface: %d\n\n\n", n->ifindex);

	// char *	ptr = n->next_hop;
	// int j = HW_ADDR_LEN;
	// do 
	// {
	// 	printf("%.2x%s", *ptr++ & 0xff, (j == 1) ? " " : ":");
	// } while (--j > 0);

	// printf("\n\n\n");

        for(i = 0; i < num_sockets; i++ )
        {
            if(sockets[i].ifindex == n->ifindex)
            {
                break;
            }
        }


    send_unicast(sockets[i].sockfd, sockets[i].mac_addr, n->next_hop,
            n->ifindex, (void*) app_data, sizeof(APP_hdr));
}

char *
create_stream_msg_recv(
        APP_hdr* app_data
        )
{
    char * stream = (char *) malloc(1000);
    char source_id[INET_ADDRSTRLEN];

    Inet_ntop(AF_INET, &app_data->source_id.s_addr, source_id, sizeof(source_id));
		
    bzero(stream, 1000);

    sprintf(stream, "%d %s %s",ntohs(app_data->source_port),source_id, app_data->data);

    return stream;
}

void 
msg_receive(
        int sockfd, 
        char msg[],
        char addr[INET_ADDRSTRLEN], 
        int * portno 
        )

{
    char char_seq[1000] = "\0";     
    char data_from_client[20];
    struct sockaddr_un odraddr;
    char buf[1000];
    int addr_len;
    int message_received;

    struct sockaddr_in sa, sa_self;

    char recvd_vm[10], sent_vm[10];

    message_received = recvfrom(sockfd, buf, 1000, 0, (SA*) &odraddr, &addr_len);
    check_err(message_received, "receive error");
       
    strcpy(data_from_client, parse_msg_receive_data(buf, addr, portno));

    if (*portno == SERV_PORT)
    {
        printf("\nClient at node %s: ", get_vmname_from_ip(get_odr_ip()));
        printf("received from %s <%s>\n", get_vmname_from_ip(addr), data_from_client);
     }
    else
    {
        printf("\nServer at %s ", get_vmname_from_ip(get_odr_ip()));
        printf("responding to request from %s\n", get_vmname_from_ip(addr));
    }
}

char *
get_vmname_from_ip(
        char ip[]
        )
{
    struct sockaddr_in sa;
    struct hostent *he = NULL;

//    printf("Querying %s.\n", ip);
    Inet_pton(AF_INET, ip, &(sa.sin_addr));

    he = gethostbyaddr((char *) &sa.sin_addr.s_addr, sizeof(sa.sin_addr.s_addr), AF_INET);

    return he->h_name;
}

char *  
parse_msg_receive_data( 
        char msg[1000],
        char ip_addr[INET_ADDRSTRLEN],
        int * portno
        )
{
    char * temp = (char *) malloc (1000);
    int c = 0;
    int i;
    int j;

    bzero(temp, 1000);

    // printf("parsing %s.\n", msg);

    for (i = 0, j = 0 ; i < strlen(msg); i++)
    {
        if((msg[i] == ' ' && c != 2) || (i == strlen(msg) - 1))
        {
            c++;
            if(i == strlen(msg) - 1)
            {
                temp[j] = msg[i];
                j++;
            }
            temp[j] = '\0';
            switch(c)
            {
                case 1:
//                    printf("temp: %s.\n", temp);
                    * portno = atoi(temp);
                    break;			

                case 2:
                    strcpy(ip_addr, temp);
                    break;

                case 3:
                    return temp;
            }	
            j = 0;
        }
        else
        {
            temp[j] = msg[i];
            j++;
        }	
    }

    return temp;
}

int isstale(struct timeval ts, int staleness, char * dest)
{
    struct timeval curr;	
    gettimeofday(&curr, NULL);

    if(curr.tv_sec - ts.tv_sec > staleness)
    {
		printf("\nCurrent Timestamp = %d Timestamp for the Destination %s = %d Difference = %d > staleness (= %d)\n", curr.tv_sec, dest, ts.tv_sec, curr.tv_sec - ts.tv_sec, staleness);

		return 1;
    }
    else
    {
		printf("\nCurrent Timestamp = %d Timestamp for the Destination %s = %d Difference = %d < staleness (= %d)\n", curr.tv_sec, dest, ts.tv_sec, curr.tv_sec - ts.tv_sec, staleness);

		return 0;
    }	
}

